package edu.bbu.commonproject.desktop.gui.controller.concrete;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

import edu.bbu.commonproject.desktop.client.exceptions.DataBaseErrorException;
import edu.bbu.commonproject.desktop.client.exceptions.DecryptErrorException;
import edu.bbu.commonproject.desktop.client.exceptions.EmailSendingException;
import edu.bbu.commonproject.desktop.client.exceptions.NoConnectionException;
import edu.bbu.commonproject.desktop.client.exceptions.WrongUserDataException;
import edu.bbu.commonproject.desktop.client.model.UserPOJO;
import edu.bbu.commonproject.desktop.gui.controller.BaseController;
import edu.bbu.commonproject.desktop.gui.controller.utils.CryptingService;
import edu.bbu.commonproject.desktop.gui.controller.utils.PasswordGenerator;
import edu.bbu.commonproject.desktop.gui.controller.utils.StringFormatter;
import edu.bbu.commonproject.desktop.gui.controller.utils.UserProperties;
import edu.bbu.commonproject.desktop.gui.view.LoginInterface;
import edu.bbu.commonproject.desktop.gui.view.swing.base.BaseFrame;
import edu.bbu.commonproject.desktop.gui.view.swing.utils.AboutFrame;
import edu.bbu.commonproject.desktop.gui.view.swing.utils.HelpFrame;
import edu.bbu.commonproject.desktop.gui.view.swing.utils.Tray;

/**
 * The log on GUI component controller.
 */
public class LogonController extends BaseController
{
	/** The base frame controller. */
	private BaseFrameController baseFrameController;
	
	/** The login interface. */
	private LoginInterface logonInterface;
	
	/** The encryption. */
	private CryptingService cryptingService;
	
	/** The password generator. */
	private PasswordGenerator passwordGenerator;
	
	/**
	 * The log on controller's constructor.
	 * @param logonView the GUI elements interface.
	 */
	public LogonController(BaseFrameController baseFrameController, LoginInterface logonView)
	{
		this.logonInterface = logonView;
		this.baseFrameController = baseFrameController;
		
		try
		{
			this.cryptingService = new CryptingService();
		}
		catch (InvalidKeyException | NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeySpecException | NoSuchPaddingException e)
		{
			e.printStackTrace();
		}
		
		passwordGenerator = new PasswordGenerator();
		
		setListeners();
	}
	
	/**
	 * Setting on the listeners.
	 */
	private void setListeners()
	{
		setNavigationMenuListeners();
		
		setLogonViewListeners();
		setRegisterViewListeners();
		setForgotPasswordViewListeners();
		
		setMenuBarListeners();
		setTrayPopupMenuListesners();
	}
	
	/**
	 * Setting up the listeners for the view changers.
	 */
	private void setNavigationMenuListeners()
	{
		((JButton) logonInterface.getLoginViewChangeButton()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringLoginViewToFront();
			}
		});
		
		((JButton) logonInterface.getRegisterViewChangeButton()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringRegisterViewToFront();
			}
		});
		
		((JButton) logonInterface.getForgotPasswordViewChangeButton()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringForgotViewToFront();
			}
		});
	}
	
	/**
	 * Setting up the listers for the login.
	 */
	private void setLogonViewListeners() {
		
		((JButton)logonInterface.getLoginUserButton()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				loginUser();
			}
		});
		
		((JTextField) logonInterface.getLoginUserNameTextField()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				loginUser();
			}
		});
		
		((JPasswordField) logonInterface.getLoginPasswordTextField()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				loginUser();
			}
		});
	}
	
	/**
	 * Setting up the listeners for the registering.
	 */
	private void setRegisterViewListeners() 
	{
		((JButton)logonInterface.getRegisterButton()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				registerUser();
			}
		});
		
		((JTextField) logonInterface.getRegisterEmailTextField()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				registerUser();
			}
		});
		
		((JTextField) logonInterface.getRegisterConfirmEmailTextField()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				registerUser();
			}
		});
	}
	
	/**
	 * Setting up the listeners for the forgot password.
	 */
	private void setForgotPasswordViewListeners() 
	{
		((JButton)logonInterface.getForgotPasswordButton()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{			
				forgotPassword();
			}
		});
		
		((JTextField)logonInterface.getForgotPasswordTextField()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{			
				forgotPassword();
			}
		});
	}
	
	/**
	 * Setting up the listeners for the menu bar.
	 */
	private void setMenuBarListeners()
	{
		((JMenuItem) logonInterface.getLogonMenuItem()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringLoginViewToFront();
			}
		});
		
		((JMenuItem) logonInterface.getFrogotMenuItem()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringRegisterViewToFront();
			}
		});
		
		((JMenuItem) logonInterface.getRegisterMenuItem()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringForgotViewToFront();
			}
		});
		
		((JMenuItem) logonInterface.getExitMenuItem()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				System.exit(0);
			}
		});
		
		((JMenuItem) logonInterface.getHelpMenuItem()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				new HelpFrame();
			}
		});
		
		((JMenuItem) logonInterface.getAboutMenuItem()).addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				new AboutFrame();
			}
		});
	}
	
	/**
	 * Setting the listeners for the for the tray.
	 */
	private void setTrayPopupMenuListesners()
	{
		((Tray) baseFrameController.getBaseFrame().getTrayIcon()).getLoginItem().addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringLoginViewToFront();
				baseFrameController.bringBaseFrameToFront();
			}
		});
		
		((Tray) baseFrameController.getBaseFrame().getTrayIcon()).getRegisterItem().addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringRegisterViewToFront();
				baseFrameController.bringBaseFrameToFront();
			}
		});
		
		((Tray) baseFrameController.getBaseFrame().getTrayIcon()).getForgotItem().addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				bringForgotViewToFront();
				baseFrameController.bringBaseFrameToFront();
			}
		});
		
		((Tray) baseFrameController.getBaseFrame().getTrayIcon()).getGuestAboutItem().addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				baseFrameController.bringBaseFrameToFront();
				new AboutFrame();
			}
		});
		
		((Tray) baseFrameController.getBaseFrame().getTrayIcon()).getGuestHelpItem().addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				baseFrameController.bringBaseFrameToFront();
				new HelpFrame();
			}
		});
		
		((Tray) baseFrameController.getBaseFrame().getTrayIcon()).getGuestExitItem().addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				System.exit(0);
			}
		});
	}

	/**
	 * Bring login view to front.
	 */
	private void bringLoginViewToFront()
	{
		((JTextField) logonInterface.getLoginUserNameTextField()).setText("");
		((JPasswordField) logonInterface.getLoginPasswordTextField()).setText("");
		((JLabel) logonInterface.getLoginErrorLabel()).setVisible(false);
		
		((JLayeredPane) logonInterface.getCollectionPanel()).moveToFront((JPanel) logonInterface.getLoginPanel());
		((JPanel) logonInterface.getLoginPanel()).setVisible(true);
		((JPanel) logonInterface.getRegisterPanel()).setVisible(false);
		((JPanel) logonInterface.getForgotPasswordPanel()).setVisible(false);		
	}
	
	/**
	 * Bring register view to front.
	 */
	private void bringRegisterViewToFront()
	{
		((JTextField) logonInterface.getRegisterEmailTextField()).setText("");
		((JTextField) logonInterface.getRegisterConfirmEmailTextField()).setText("");
		((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(false);
		
		((JLayeredPane) logonInterface.getCollectionPanel()).moveToFront((JPanel) logonInterface.getRegisterPanel());
		((JPanel) logonInterface.getLoginPanel()).setVisible(false);
		((JPanel) logonInterface.getRegisterPanel()).setVisible(true);
		((JPanel) logonInterface.getForgotPasswordPanel()).setVisible(false);	
	}
	
	/**
	 * Bring forgot view to front.
	 */
	private void bringForgotViewToFront()
	{
		((JTextField) logonInterface.getForgotPasswordTextField()).setText("");
		((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(false);
		
		((JLayeredPane) logonInterface.getCollectionPanel()).moveToFront((JPanel) logonInterface.getForgotPasswordPanel());
		((JPanel) logonInterface.getLoginPanel()).setVisible(false);
		((JPanel) logonInterface.getRegisterPanel()).setVisible(false);
		((JPanel) logonInterface.getForgotPasswordPanel()).setVisible(true);
	}
	
	/**
	 * Logging in the user.
	 */
	private void loginUser()
	{
		((JLabel) logonInterface.getLoginErrorLabel()).setVisible(false);
		
		String userName = stringFormatter.userNameFormat(((JTextField) logonInterface.getLoginUserNameTextField()).getText());
		String password = new String(((JPasswordField) logonInterface.getLoginPasswordTextField()).getPassword());
		
		if(userName.length() == 0)
		{
			((JLabel) logonInterface.getLoginErrorLabel()).setText("<html><center>Username field is empty.</center></html>");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(true);
			
			return;
		}
		
		if(password.length() == 0)
		{
			((JLabel) logonInterface.getLoginErrorLabel()).setText("<html><center>Password field is empty.</center></html>");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(true);
			
			return;
		}
		
		try
		{
			String cryptedPassword = cryptingService.encrypt(password);
			UserPOJO userPOJO = new UserPOJO(userName, cryptedPassword);
			
			userServices.logonUser(userPOJO);
			
			userProperties = new UserProperties(userName);
			baseFrameController.getFileListController().logonUser();
			baseFrameController.getBaseFrame().moveToFront(BaseFrame.FILES_PANEL);
			
			((JTextField) logonInterface.getLoginUserNameTextField()).setText("");
			((JPasswordField) logonInterface.getLoginPasswordTextField()).setText("");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(false);
			
			((JLayeredPane) logonInterface.getCollectionPanel()).moveToFront((JPanel) logonInterface.getLoginPanel());
			((JPanel) logonInterface.getLoginPanel()).setVisible(true);
			((JPanel) logonInterface.getRegisterPanel()).setVisible(false);
			((JPanel) logonInterface.getForgotPasswordPanel()).setVisible(false);
		}
		catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException ex) 
		{
			((JLabel) logonInterface.getLoginErrorLabel()).setText("<html><center>Password crypting error.Try again.</center></html>");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(true);
		}
		catch (NoConnectionException ex)
		{
			((JLabel) logonInterface.getLoginErrorLabel()).setText("<html><center>Connection timeout.</center></html>");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(true);
		}
		catch (WrongUserDataException ex)
		{
			((JLabel) logonInterface.getLoginErrorLabel()).setText("<html><center>Incorrect username or password.</center></html>");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(true);
		}
		catch (DataBaseErrorException ex)
		{
			((JLabel) logonInterface.getLoginErrorLabel()).setText("<html><center>Database error.Try again.</center></html>");
			((JLabel) logonInterface.getLoginErrorLabel()).setVisible(true);
		}
	}
	
	/**
	 * Registering the user.
	 */
	private void registerUser()
	{
		((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(false);
		
		String userName = stringFormatter.userNameFormat(((JTextField) logonInterface.getRegisterEmailTextField()).getText());
		String userNameConfirmationString = stringFormatter.userNameFormat(((JTextField) logonInterface.getRegisterConfirmEmailTextField()).getText());
		String password = passwordGenerator.generatePassword(16);
		
		if(userName.length() == 0)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>E-mail adress field is empty.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
			
			return;
		}
		
		if(userNameConfirmationString.length() == 0)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Confirmation e-mail adress field is empty.</center><html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
			
			return;
		}
		
		if(!userName.equals(userNameConfirmationString))
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Different e-mail adresses. E-mail adresses must be the same.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
			
			return;
		}
				
		try
		{
			String cryptedPassword = cryptingService.encrypt(password);
			
			UserPOJO userPOJO = new UserPOJO(userName, cryptedPassword);
			BaseController.userServices.registerUser(userPOJO);
			
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Registration succesfull. Please check your e-mail adress for password.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
			
		}
		catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException ex) 
		{
			ex.printStackTrace();
		}
		catch (NoConnectionException ex)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Connection timeout.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
		}
		catch (DecryptErrorException ex)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Password crypting error.Try again.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
		}
		catch (WrongUserDataException ex)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>E-mail adress already in use. Please select a different one.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
		}
		catch (DataBaseErrorException ex)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Database error.Try again.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
		}
		catch (EmailSendingException ex)
		{
			((JLabel) logonInterface.getRegisterErrorLabel()).setText("<html><center>Email sending failed.Try again.</center></html>");
			((JLabel) logonInterface.getRegisterErrorLabel()).setVisible(true);
		}
	}
	
	/**
	 * Handle the forgot password.
	 */
	private void forgotPassword()
	{
		((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(false);
		
		String userName = stringFormatter.userNameFormat(((JTextField)logonInterface.getForgotPasswordTextField()).getText());
		String newPassword = passwordGenerator.generatePassword(16);;
		
		if(userName.length() == 0)
		{
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>E-mail adress filed is empty.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
			
			return;
		}
		
		try
		{
			String cryptedNewPassword = cryptingService.encrypt(newPassword);
			
			UserPOJO userPOJO = new UserPOJO(userName, "forgottenPassword");
			BaseController.userServices.forgotPassword(userPOJO, cryptedNewPassword);
			
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>New password have been sent to your e-mail adress.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
			
		}
		catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException ex) 
		{
			ex.printStackTrace();
		}
		catch (NoConnectionException ex)
		{
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>Connection timeout.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
		}
		catch (DecryptErrorException ex)
		{
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>Password crypting error.Try again.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
		}
		catch (WrongUserDataException ex)
		{
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>Invalid e-mail adress.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
		}
		catch (DataBaseErrorException ex)
		{
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>Database error.Try again.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
		}
		catch (EmailSendingException ex)
		{
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setText("<html><center>Email sending failed.Try again.</center></html>");
			((JLabel) logonInterface.getForgotPasswordErrorLabel()).setVisible(true);
		}
	}
}
